﻿using System;
using System.Text;
using Microsoft.Xna.Framework.Input;

    [Flags]
    public enum KeyModifiers: int
    {
        None = 0x00,
        LeftControl = 0x01,
        RightControl = 0x02,
        Control = 0x03,
        LeftAlt = 0x04,
        RightAlt = 0x08,
        Alt = 0x0c,
        LeftShift = 0x10,
        RightShift = 0x20,
        Shift = 0x30,
    }

    public struct KeyData
    {
        public Keys Key;
        public KeyModifiers Modifier;

        public KeyData(Keys key, KeyModifiers keyMod)
        {
            Key = key;
            Modifier = keyMod;
        }
    }

    public class InputMessageFilter: System.Windows.Forms.IMessageFilter
    {
        #region Properties
        private bool enabled;

        public bool Enabled
        {
            get { return enabled; }
            set { enabled = value; }
        }

        private bool translateMessage;

        public bool TranslateMessage
        {
            get { return translateMessage; }
            set { translateMessage = value; }
        }

        private Stack<KeyData> keyData;

        public Stack<KeyData> KeyData
        {
            get { return keyData; }
            set { keyData = value; }
        }

        private StringBuilder _text;

        public StringBuilder Text
        {
            get { return _text; }
            set { _text = value; }
        }

        public int Count { get { return KeyData.Count; } }
        #endregion

        public InputMessageFilter()
        {
            KeyData = new Stack<KeyData>();
            Text = new StringBuilder(); 
            System.Windows.Forms.Application.AddMessageFilter( this );
        }

        public string GetText()
        {
            string text = Text.ToString();
            Text.Length = 0;
            return text;
        }

        #region IMessageFilter Members

        #region Nested

        protected enum Wm
        {
            Active = 6,
            Char = 0x102,
            KeyDown = 0x100,
            KeyUp = 0x101,
            SysKeyDown = 260,
            SysKeyUp = 0x105
        }

        protected enum Wa
        {
            Inactive,
            Active,
            ClickActive
        }

        protected enum Vk
        {
            Alt = 0x12,
            Control = 0x11,
            Shift = 0x10
        }

        #endregion

        #region Interop

        [System.Runtime.InteropServices.DllImport( "user32.dll", EntryPoint = "TranslateMessage" )]
        protected extern static bool _TranslateMessage( ref System.Windows.Forms.Message m );

        #endregion

        KeyModifiers modifier;

        bool System.Windows.Forms.IMessageFilter.PreFilterMessage( ref System.Windows.Forms.Message m )
        {
            if ( !Enabled )
                return false;
            //
            switch ( (Wm)m.Msg )
            {
            case Wm.SysKeyDown:
            case Wm.KeyDown:
                if ( (m.LParam.ToInt32() & (1 << 30)) == 0 )//iff repeat count == 0
                {
                    KeyData data;
                    switch ( (Vk)m.WParam )
                    {
                    case Vk.Control:
                        if ( (m.LParam.ToInt32() & (1 << 24)) == 0 )
                        {
                            data = new KeyData(Keys.LeftControl, modifier);
                            KeyData.Push( ref data );
                            modifier |= KeyModifiers.LeftControl;
                        }
                        else
                        {
                            data = new KeyData(Keys.RightControl, modifier);
                            KeyData.Push( ref data );
                            modifier |= KeyModifiers.RightControl;
                        }
                        break;
                    case Vk.Alt:
                        if ( (m.LParam.ToInt32() & (1 << 24)) == 0 )
                        {
                            data = new KeyData(Keys.LeftAlt, modifier);
                            KeyData.Push( ref data );
                            modifier |= KeyModifiers.LeftAlt;
                        }
                        else
                        {
                            data = new KeyData(Keys.RightAlt, modifier);
                            KeyData.Push( ref data );
                            modifier |= KeyModifiers.RightAlt;
                        }
                        break;
                    case Vk.Shift:
                        if ( (m.LParam.ToInt32() & (1 << 24)) == 0 )
                        {
                            data = new KeyData(Keys.LeftShift, modifier);
                            KeyData.Push( ref data );
                            modifier |= KeyModifiers.LeftShift;
                        }
                        else
                        {
                            data = new KeyData(Keys.RightShift, modifier);
                            KeyData.Push( ref data );
                            modifier |= KeyModifiers.RightShift;
                        }
                        break;
                    //
                    default:
                        data = new KeyData((Keys)m.WParam, modifier);
                        KeyData.Push( ref data );
                        break;
                    }
                }
                //
                if ( TranslateMessage )
                    _TranslateMessage( ref m );
                //
                return true;//Hide from Forms
                
            case Wm.SysKeyUp:
            case Wm.KeyUp:
                switch ( (Vk)m.WParam )
                {
                case Vk.Control:
                    if ( (m.LParam.ToInt32() & (1 << 24)) == 0 )
                        modifier &= ~KeyModifiers.LeftControl;
                    else
                        modifier &= ~KeyModifiers.RightControl;
                    break;
                case Vk.Alt:
                    if ( (m.LParam.ToInt32() & (1 << 24)) == 0 )
                        modifier &= ~KeyModifiers.LeftAlt;
                    else
                        modifier &= ~KeyModifiers.RightAlt;
                    break;
                case Vk.Shift:
                    if ( (m.LParam.ToInt32() & (1 << 24)) == 0 )
                        modifier &= ~KeyModifiers.LeftShift;
                    else
                        modifier &= ~KeyModifiers.RightShift;
                    break;
                }
                return true;

            case Wm.Char:
                //
                char c = (char)m.WParam;
                if ( c < (char)0x20
                    && c != '\n'
                    && c != '\r'
                    //&& c != '\t'//tab //uncomment to accept tab
                    && c != '\b' )//backspace
                    break;
                //
                if ( c == '\r' )
                    c = '\n';//Note: Control+ENTER will send \n, just ENTER will send \r
                //
                if ( c == '\b' && Text.Length > 0 && Text[Text.Length - 1] != '\b' )
                    Text.Length--;//pop 1
                //
                Text.Append( c );
                return true;

            case Wm.Active:
                if ( ((int)m.WParam & 0xffff) == (int)Wa.Inactive )
                {
                    modifier = KeyModifiers.None;
                }
                break;//Must not filter
            }
            return false;
        }

        #endregion
    }